Emberveil sourcegame/2006Scape Server/src/main/java/com/rs2/GameEngine.java
package com.rs2;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import com.google.common.base.Stopwatch;
import com.rs2.game.npcs.Npc;
import com.rs2.game.npcs.NpcList;
import com.rs2.gui.ControlPanel;
import com.rs2.util.CustomPrintStream;
import org.apollo.cache.IndexedFileSystem;
import org.apollo.cache.decoder.ItemDefinitionDecoder;
import org.apollo.cache.decoder.ObjectDefinitionDecoder;
import org.apollo.jagcached.FileServer;
import com.rs2.game.bots.BotHandler;
import com.rs2.event.CycleEventHandler;
import com.rs2.game.content.minigames.FightCaves;
import com.rs2.game.content.minigames.FightPits;
import com.rs2.game.content.minigames.PestControl;
import com.rs2.game.content.minigames.castlewars.CastleWars;
import com.rs2.game.content.minigames.magetrainingarena.MageTrainingArena;
import com.rs2.game.content.minigames.trawler.Trawler;
import com.rs2.game.globalworldobjects.Doors;
import com.rs2.game.globalworldobjects.DoubleDoors;
import com.rs2.game.items.ItemDefinitions;
import com.rs2.game.npcs.NpcHandler;
import com.rs2.game.players.Client;
import com.rs2.game.players.Player;
import com.rs2.game.players.PlayerHandler;
import com.rs2.game.players.PlayerSave;
import com.rs2.game.shops.ShopHandler;
import com.rs2.integrations.PlayersOnlineWebsite;
import com.rs2.integrations.RegisteredAccsWebsite;
import com.rs2.integrations.discord.DiscordActivity;
import com.rs2.integrations.discord.JavaCord;
import com.rs2.util.HostBlacklist;
import com.rs2.world.GlobalDropsHandler;
import com.rs2.world.ItemHandler;
import com.rs2.world.ObjectHandler;
import com.rs2.world.ObjectManager;
import com.rs2.world.clip.RegionFactory;
import io.netty.util.ResourceLeakDetector;
import io.netty.util.ResourceLeakDetector.Level;
import org.apollo.net.NetworkConstants;
/**
* Server.java
*
* @author Sanity
* @author Graham
* @author Blake
* @author Ryan Lmctruck30
* @author Integration Julian.
*/
public class GameEngine {
private static long minutesCounter;
private static void startMinutesCounter() {
try {
minuteFile = new BufferedReader(new FileReader(
Constants.SERVER_LOG_DIR + "minutes.log"));
minutesCounter = Long.parseLong(minuteFile.readLine());
} catch (Exception e) {
e.printStackTrace();
}
}
private static void setMinutesCounter(long minutesCounter) {
try {
BufferedWriter minuteCounter = new BufferedWriter(new FileWriter(
Constants.SERVER_LOG_DIR + "minutes.log"));
minuteCounter.write(Long.toString(minutesCounter));
minuteCounter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static long getMinutesCounter() {
long d = TimeUnit.MILLISECONDS.toMinutes(System.currentTimeMillis()
- minutesCounter);
return d;
}
public static String ersSecret;
public static int[] cannonsX = new int[50];
public static int[] cannonsY = new int[50];
public static String[] cannonsO = new String[50];
public static boolean sleeping;
public static boolean updateServer = false;
public static long lastMassSave = System.currentTimeMillis();
public static boolean shutdownServer = false;
public static int garbageCollectDelay = 40;
public static boolean shutdownClientHandler;
public static ItemHandler itemHandler = new ItemHandler();
public static PlayerHandler playerHandler = new PlayerHandler();
public static NpcHandler npcHandler = new NpcHandler();
public static ShopHandler shopHandler = new ShopHandler();
public static ObjectHandler objectHandler = new ObjectHandler();
public static ObjectManager objectManager = new ObjectManager();
public static FightCaves fightCaves = new FightCaves();
private static PestControl pestControl = new PestControl();
public static Trawler trawler = new Trawler();
private final static ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
private final static Lock lock = new ReentrantLock();
public static ControlPanel panel;
private static long serverStartTime;
static {
shutdownServer = false;
}
public static void main(java.lang.String[] args)
throws NullPointerException, IOException {
CustomPrintStream errorStream = new CustomPrintStream(System.err, "ERROR", true);
System.setErr(errorStream);
CustomPrintStream infoStream = new CustomPrintStream(System.out, "INFO", true);
System.setOut(infoStream);
serverStartTime = System.currentTimeMillis();
if (NetworkConstants.RSA_EXPONENT != Constants.RSA_EXPONENT) {
NetworkConstants.RSA_EXPONENT = Constants.RSA_EXPONENT;
NetworkConstants.RSA_MODULUS = Constants.RSA_MODULUS;
}
for (int i = 0; i < args.length; i++) {
if (args[i].equals("-gui"))
Constants.GUI_ENABLED = true;
if (args[i].startsWith("-") && (i + 1) < args.length && !args[i + 1].startsWith("-")) {
switch (args[i]) {
case "-c":
case "-config":
try {
System.out.println("Loading External Config..");
ConfigLoader.loadSettings(args[++i]);
System.out.println("Loaded Config File " + args[i]);
} catch (IOException e) {
System.out.println("Config File Not Found");
}
break;
}
}
}
System.out.println("Starting game engine..");
if (Constants.SERVER_DEBUG) {
System.out.println("@@@@ DEBUG MODE IS ENABLED @@@@");
}
if (!new File("data").exists()) {
System.err.println("************************************");
System.err.println("************************************");
System.err.println("************************************");
System.err.println("WARNING: I could not find the /data folder. You are LIKELY running this in the wrong directory!");
System.err.println("In IntelliJ, fix it by clicking \"Server\" > Edit Configurations at the top of your screen");
System.err.println("Then changing the \"Working Directory\" to be in \"2006Scape/2006Scape Server\", instead of just \"2006Scape\"");
System.err.println("************************************");
System.err.println("************************************");
System.err.println("************************************");
System.exit(1);
}
/**
* Starting Up Server
*/
System.out.println("Launching " + Constants.SERVER_NAME + " World: " + Constants.WORLD + "...");
/**
* Start Integration Services
**/
ConfigLoader.loadSecrets();
JavaCord.init();
/**
* Accepting Connections
*/
//TODO debug ResourceLeakDetector.setLevel(Level.PARANOID);
ResourceLeakDetector.setLevel(Level.DISABLED);
FileServer fs = new FileServer();
try {
fs.start();
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
IndexedFileSystem cache = new IndexedFileSystem(Paths.get(Constants.FILE_SYSTEM_DIR), true);
new ObjectDefinitionDecoder(cache).run();
new ItemDefinitionDecoder(cache).run();
/**
* Initialise Handlers
*/
RegionFactory.load(cache);
Doors.getSingleton().load();
DoubleDoors.getSingleton().load();
ItemDefinitions.load();
GlobalDropsHandler.initialize();
Connection.initialize();
HostBlacklist.loadBlacklist();
BotHandler.loadPlayerShops();
startMinutesCounter();
setMinutesCounter(minutesCounter);
/**
* Load Plugins
*/
Player.getPluginService().load();
/**
* Server Successfully Loaded
*/
System.out.println("World Server listening on " + fs.service.toString());
/**
* Makes Visible Control Panel If Enabled
*/
if(Constants.GUI_ENABLED) {
ControlPanel panel = new ControlPanel();
panel.initComponents();
panel.setVisible(true);
System.out.println("Control Panel Enabled.");
}
/**
* Main Server Tick
*
* This scheduler will tick once every 600ms. If the previous tick takes
* 300ms to execute, this scheduler will wait 300ms only before the next
* tick.
*
* scheduleAtFixedRate() does not invoke concurrent Runnables.
*/
scheduler.scheduleAtFixedRate(new Runnable() {
int gameTicksIncrementor;
final int printInfoTick = Constants.CYCLE_LOGGING_TICK;
public void run() {
if (com.rs2.game.content.emberveil.ServerControl.poll()) {
scheduler.shutdown();
return;
}
Stopwatch stopwatch = Stopwatch.createStarted();
/**
* Main Server Tick
*/
try {
if (GameEngine.shutdownServer) {
scheduler.shutdown();
}
long startItemHandler = System.currentTimeMillis();
itemHandler.process();
long durationItemHandler = System.currentTimeMillis() - startItemHandler;
checkAndLogDuration("ItemHandler", durationItemHandler);
long startPlayerHandler = System.currentTimeMillis();
playerHandler.process();
com.rs2.game.content.emberveil.PublicServerStatus.publish();
com.rs2.game.content.emberveil.PlayerInformation.update();
long durationPlayerHandler = System.currentTimeMillis() - startPlayerHandler;
checkAndLogDuration("PlayerHandler", durationPlayerHandler);
long startNpcHandler = System.currentTimeMillis();
npcHandler.process();
long durationNpcHandler = System.currentTimeMillis() - startNpcHandler;
checkAndLogDuration("NpcHandler", durationNpcHandler);
long startShopHandler = System.currentTimeMillis();
shopHandler.process();
long durationShopHandler = System.currentTimeMillis() - startShopHandler;
checkAndLogDuration("ShopHandler", durationShopHandler);
long startObjectManager = System.currentTimeMillis();
objectManager.process();
long durationObjectManager = System.currentTimeMillis() - startObjectManager;
checkAndLogDuration("ObjectManager", durationObjectManager);
long startCastleWars = System.currentTimeMillis();
CastleWars.process();
long durationCastleWars = System.currentTimeMillis() - startCastleWars;
checkAndLogDuration("CastleWars", durationCastleWars);
long startFightPits = System.currentTimeMillis();
FightPits.process();
long durationFightPits = System.currentTimeMillis() - startFightPits;
checkAndLogDuration("FightPits", durationFightPits);
long startPestControl = System.currentTimeMillis();
pestControl.process();
long durationPestControl = System.currentTimeMillis() - startPestControl;
checkAndLogDuration("PestControl", durationPestControl);
long startObjectHandler = System.currentTimeMillis();
objectHandler.process();
long durationObjectHandler = System.currentTimeMillis() - startObjectHandler;
checkAndLogDuration("CastleWars", durationObjectHandler);
long startMageTrainingArena = System.currentTimeMillis();
MageTrainingArena.process();
long durationMageTrainingArena = System.currentTimeMillis() - startMageTrainingArena;
checkAndLogDuration("MageTrainingArena", durationMageTrainingArena);
long startCycleEventHandler = System.currentTimeMillis();
CycleEventHandler.getSingleton().process();
long durationCycleEventHandler = System.currentTimeMillis() - startCycleEventHandler;
checkAndLogDuration("CycleEventHandler", durationCycleEventHandler);
long startIntegrationEvents = System.currentTimeMillis();
if (Constants.WEBSITE_INTEGRATION) {
PlayersOnlineWebsite.addUpdatePlayersOnlineTask();
RegisteredAccsWebsite.addUpdateRegisteredUsersTask();
}
if (DiscordActivity.playerCount) {
DiscordActivity.updateActivity();
}
long durationIntegrationEvents = System.currentTimeMillis() - startIntegrationEvents;
checkAndLogDuration("IntegrationEvents", durationIntegrationEvents);
long startSaveEvents = System.currentTimeMillis();
if (System.currentTimeMillis() - lastMassSave > 300000) {
for (Player p : PlayerHandler.players) {
if (p == null) {
continue;
}
PlayerSave.saveGame((Client) p);
System.out.println("Saved game for " + p.playerName + ".");
lastMassSave = System.currentTimeMillis();
}
}
long durationSaveEvents = System.currentTimeMillis() - startSaveEvents;
checkAndLogDuration("SaveEvents", durationSaveEvents);
long totalCycleDuration = stopwatch.elapsed(TimeUnit.MILLISECONDS);
//Technically, we could add commands to test both client lag (creating many tile objects) and server lag (creating a BCrypt hash on game thread)
if (totalCycleDuration > 500) {
System.err.println("ERROR: Cycle duration exceeded 500 ms!");
} else if (totalCycleDuration > 250) {
System.err.println("WARNING: Cycle duration exceeded 250 ms!");
} else if (totalCycleDuration > 100) {
System.out.println("NOTICE: Cycle duration exceeded 100 ms.");
}
gameTicksIncrementor++;
if (Constants.CYCLE_LOGGING && gameTicksIncrementor > 1 && gameTicksIncrementor % printInfoTick == 0) {
long totalMem = Runtime.getRuntime().totalMemory();
long freeMem = Runtime.getRuntime().freeMemory();
long maxMem = Runtime.getRuntime().maxMemory();
int playerCount = 0;
for (Player p : PlayerHandler.players) {
if (p != null) {
playerCount++;
}
}
int npcCount = 0;
for (Npc npc : NpcHandler.npcs) {
if (npc != null) {
npcCount++;
}
}
System.out.println("Cycle #" + gameTicksIncrementor + " took " + totalCycleDuration + " ms. Players: " + playerCount + ", NPCs: " + npcCount +
", [Durations: i: " + durationItemHandler + " ms, p: " + durationPlayerHandler + " ms, n: " + durationNpcHandler + " ms, s: " + durationShopHandler +
" ms, oh: " + durationObjectHandler + " ms, om: " + durationObjectManager + " ms], Memory: " + (totalMem - freeMem) / 1024 / 1024 + "MB/" +
totalMem / 1024 / 1024 + "MB. Max: " + maxMem / 1024 / 1024 + "MB, Threads: " + Thread.activeCount() + ".");
}
} catch (Exception ex) {
ex.printStackTrace();
System.err.println("A fatal exception has been thrown in the GameEngine cycle! Saving all players.");
for (Player p : PlayerHandler.players) {
if (p == null) {
continue;
}
if (p.inTrade) {
((Client) p).getTrading().declineTrade();
}
if (p.duelStatus == 6) {
((Client) p).getDueling().claimStakedItems();
}
PlayerSave.saveGame((Client) p);
System.out.println("Saved game for " + p.playerName + ".");
}
scheduler.shutdown(); // Kills the tickloop thread if Exception is thrown.
}
}
}, 0, Constants.CYCLE_TIME, TimeUnit.MILLISECONDS);
/*
* I'd recommend disabling this until I can be bothered to implement it
* properly.
*/
// CommandConsole.getInstance();
try {
while (!scheduler.awaitTermination(60, TimeUnit.SECONDS)) {
// TODO
// Cleanup?
}
} catch (InterruptedException e) {
e.printStackTrace();
}
System.exit(0);
}
private static void checkAndLogDuration(String processName, long duration) {
if (duration > 500) {
System.err.println("ERROR: " + processName + " duration exceeded 500 ms! Duration: " + duration + " ms.");
} else if (duration > 250) {
System.err.println("WARNING: " + processName + " duration exceeded 250 ms! Duration: " + duration + " ms.");
} else if (duration > 100) {
System.out.println("NOTICE: " + processName + " duration exceeded 100 ms. Duration: " + duration + " ms.");
}
}
public static boolean playerExecuted = false;
private static BufferedReader minuteFile;
public static long getServerStartTime() {
return serverStartTime;
}
}